#include <glob.h>
#include <cstring>
#include <string>
#include <vector>
#include <stdexcept>
#include <iostream>
#include <sstream>
#include "cmpcxx.h"

namespace cmpcxx {

using namespace std;

// path
string get_base_main_name(const string& path) {
	string base = get_base(path);
	string base_main = get_main(base);
	return base_main;
}

string get_main(const string& path) {
	string main, ext;
	path_split_ext(path, main, ext);
	return main;
}

string get_ext(const string& path) {
	string main, ext;
	path_split_ext(path, main, ext);
	return ext;
}

string get_dir(const string& path) {
	string dir, base;
	path_split(path, dir, base);
	return dir;
}

string get_base(const string& path) {
	string dir, base;
	path_split(path, dir, base);
	return base;
}

void path_split(const string& path_ori, string& dir, string& base) {
	// "xxx/yyy//" or "xxx/yyy///" => "xxx/yyy"
	string path = path_remove_tail_slash(path_ori);

	// get last separator '/'
	bool dir_ok = false;
	char sep = '/';
	size_t pos = path.find_last_of(sep);
	if (string::npos == pos) { // path does not contain any separator
		dir = "";
		base = path; // "xxx" => "" + "xxx"
		dir_ok = true;
	} else if (0 == pos) { // path's right most separator is just its leading slash
		dir = "/";
		base = path.substr(pos + 1);  // "/xxx" => "/" + "xxx"
		dir_ok = true;
	} else {
		dir = path.substr(0, pos);
		dir = path_remove_tail_slash(dir);
		base = path.substr(pos + 1); // "xxx/yyy///zzz" => "xxx/yyy//" + "zzz" => "xxx/yyy" + "zzz"
	}
}

void path_split_ext(const string& path, string& main, string& ext) {
	string dir, base;
	path_split(path, dir, base); // path => dir + base

	// right most separator '.' determines the ext
	char sep = '.';
	size_t pos = base.find_last_of(sep);
	string base_main;
	if (string::npos == pos) { // path does not contain any separator
		base_main = base;
		ext = "";
	} else {
		base_main = base.substr(0, pos);
		ext = base.substr(pos + 1);
	}

	// determine the main name
	size_t dir_size = dir.size(), base_main_size = base_main.size();
	if (!dir_size && base_main_size) { // dir is "" and base main is not "" => main name is base main
		main = base_main;
	} else if (dir_size && !base_main_size) { // dir is not "" and base main is "" => main name is dir
		main = dir;
	} else if (!dir_size && !base_main_size) { // dir and base main are all "" => main name is ""
		main = "";
	} else { // dir + base main => main name
		if (dir[dir_size - 1] == '/') { // "xxx/" + "yyy" => "xxx/yyy"
			main = dir + base_main;
		} else { // "xxx" + "yyy" => "xxx/yyy"
			main = dir + "/" + base_main;
		}
	}

}

string path_remove_tail_slash(const string& path) {
	string buf = path;
	size_t len = buf.size();
	if (len <= 1) {
		return buf;  // "" => "", "/" => "/", "x" => "x"
	}

	if ('/' != buf.at(len - 1)) {
		return buf; // There is no / at tail!
	}

	size_t pos = buf.find_last_not_of('/');
	if (string::npos == pos) {
		return "/"; // "////" => "/"
	} else {
		return buf.substr(0, pos + 1);
	}
}

string path_remove_head_slash(const string& path) {
	string buf = path;
	size_t len = buf.size();
	if (len <= 1) {
		return buf;  // "" => "", "/" => "/", "x" => "x"
	}

	if ('/' != buf.at(0)) {
		return buf; // There is no / at head!
	}

	size_t pos = buf.find_first_not_of('/');
	if (string::npos == pos) {
		return "/"; // "////" => "/"
	} else {
		return buf.substr(pos);
	}
}

string path_join(const string& path1_ori, const string& path2_ori) {
	string path1 = path_remove_tail_slash(path1_ori); // "xxx/yyy////" => "xxx/yyy"
	string path2 = path_remove_head_slash(path2_ori); // "////xxx/yyy" => "xxx/yyy"
	size_t len1 = path1.size();
	size_t len2 = path2.size();
	if (!len1 && !len2) {
		return ""; // "" + "" => ""
	} else if (!len1) {
		return path2; // "" + "xxx" => "xxx"
	} else if (!len2) {
		return path1; // "xxx" + "" => "xxx"
	} else if (1 == len1 && '/' == path1[0] && 1 == len2 && '/' == path2[0]) { // "/" + "/" => "/"
		return "/";
	}
	string infix = "/";
	if ('/' == path1[len1 - 1] || '/' == path2[0]) {
		infix = "";
	}
	string joined = path1 + infix + path2;
	return joined;
}

// https://stackoverflow.com/questions/8401777/simple-glob-in-c-on-unix-system
// by Piti Ongmongkolkul
vector<string> glob(const string& pattern) {

    // glob struct resides on the stack
    glob_t glob_result;
    memset(&glob_result, 0, sizeof(glob_result));

    // do the glob operation
    int return_value = glob(pattern.c_str(), GLOB_TILDE, NULL, &glob_result);
    if(return_value != 0) {
        globfree(&glob_result);
        stringstream ss;
        ss << "glob() failed with return_value " << return_value << endl;
        throw runtime_error(ss.str());
    }

    // collect all the filenames into a list<string>
    vector<string> filenames;
    for(size_t i = 0; i < glob_result.gl_pathc; ++i) {
        filenames.push_back(string(glob_result.gl_pathv[i]));
    }

    // cleanup
    globfree(&glob_result);

    // done
    return filenames;
}

string getLabel(const string& filePath, bool isRemoveExt, char sep) {
	string dir, base;
	path_split(filePath, dir, base);
	if (isRemoveExt) {
		string main, ext;
		path_split_ext(base, main, ext);
		base = main;
	}

	size_t pos = base.find_last_of(sep);
	if (string::npos == pos) {
		return base;
	} else {
		string label = base.substr(pos + 1);
		return label;
	}
}

}; //namespace cmpcxx
